﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Serialization;
using Tamir.IPLib;
using Tamir.IPLib.Packets;

namespace TCPSnifferApp
{
	public partial class MainForm : Form
	{
		private const int cRefreshThreshold = 100;
		private PcapManager mPcapManager;
		private TCPSnifferDataModel mDataModel;
        private bool enter = false;
        private TCPPacket selected;

		/// <summary>
		/// Initializes a new instance of the <see cref="MainForm"/> class.
		/// </summary>
		public MainForm()
		{
			InitializeComponent();

			mDataModel = new TCPSnifferDataModel();
			mBindingSource.DataSource = mDataModel;

			mResultsView.DataError += mResultsView_DataError;

			mPcapManager = new PcapManager(TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(100));
			mPcapManager.PacketsReceived += PcapManagerPacketsReceived;

			refreshInterval.Text = mPcapManager.NotificationInterval.TotalMilliseconds.ToString();
			captureInterval.Text = mPcapManager.CaptureInterval.TotalMilliseconds.ToString();
		}

		/// <summary>
		/// Handles the DataError event of the mResultsView control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.DataGridViewDataErrorEventArgs"/> instance containing the event data.</param>
		void mResultsView_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{

		}


		/// <summary>
		/// Handles the PacketsReceived event of the mPcapManager control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="TCPSnifferApp.PacketReceivedEventArgs"/> instance containing the event data.</param>
		void PcapManagerPacketsReceived(object sender, PacketReceivedEventArgs e)
		{
			List<Packet> packets = e.Packets;

			if (packets != null && packets.Count > 0)
			{
				foreach (Packet packet in packets)
				{
					TCPPacket tcpPacket = packet as TCPPacket;
					if (InvokeRequired && tcpPacket != null)
					{
						BeginInvoke(new MethodInvoker(() => mBindingSource.Add(tcpPacket) ));
						Application.DoEvents();
						Thread.Sleep(cRefreshThreshold);
					}
				}
			}
		}

		/// <summary>
		/// Handles the Load event of the Form1 control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void Form1_Load(object sender, EventArgs e)
		{

		}

		/// <summary>
		/// Handles the Click event of the startCaptureButton control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void startCaptureButton_Click(object sender, EventArgs e)
		{
			string capture = !string.IsNullOrEmpty(captureInterval.Text) ? captureInterval.Text : "0";
			string refresh = !string.IsNullOrEmpty(refreshInterval.Text) ? refreshInterval.Text : "0";

			mPcapManager.CaptureInterval = TimeSpan.FromMilliseconds(double.Parse(capture));
			mPcapManager.NotificationInterval = TimeSpan.FromMilliseconds(double.Parse(refresh));

			mPcapManager.Start();

			startCaptureButton.Enabled = false;
			stopCaptureButton.Enabled = true;
			refreshInterval.Enabled = false;
			captureInterval.Enabled = false;
		}

		/// <summary>
		/// Handles the Click event of the stopCaptureButton control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void stopCaptureButton_Click(object sender, EventArgs e)
		{
			mPcapManager.Stop();

			startCaptureButton.Enabled = true;
			stopCaptureButton.Enabled = false;
			refreshInterval.Enabled = true;
			captureInterval.Enabled = true;
		}

		private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			mPcapManager.Stop();
		}

		private void importCaptureToolStripMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = mOpenDialog.ShowDialog();

			if (result == DialogResult.OK)
			{
				string fileName = mOpenDialog.FileName;

				if (File.Exists(fileName))
				{
					BackgroundWorker importWorker = new BackgroundWorker();
					importWorker.DoWork += ImportWorkerDoWork;
					importWorker.RunWorkerAsync(fileName);
				}
			}
		}

		void ImportWorkerDoWork(object sender, DoWorkEventArgs e)
		{
			string fileName = (string)e.Argument;
			using (Stream stream = File.OpenRead(fileName))
			{
				BinaryFormatter formatter = GetFormatter();
				TCPSnifferDataModel importedModel = (TCPSnifferDataModel)formatter.Deserialize(stream);

				if (importedModel != null)
				{
					if (InvokeRequired)
					{
						Invoke(new MethodInvoker(() => Cursor = Cursors.WaitCursor));
						Invoke(new MethodInvoker(() => mResultsView.Visible = false));
						Invoke(new MethodInvoker(() => mBindingSource.Clear()));

						for (int i = 0; i < importedModel.Count; i++)
						{
							TCPPacket packet = importedModel[i];
							BeginInvoke(new MethodInvoker(() => mBindingSource.Add(packet)));
							Application.DoEvents();
							Thread.Sleep(1);
						}
						Invoke(new MethodInvoker(() => mResultsView.Visible = true));
						Invoke(new MethodInvoker(() => Cursor = Cursors.Default));
					}
				}
			}
		}

		private void exportCaptureToolStripMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = mSaveDialog.ShowDialog();

			if (result == DialogResult.OK)
			{
				string fileName = mSaveDialog.FileName;

				if (File.Exists(fileName))
				{
					File.Delete(fileName);
				}

				using (Stream stream = File.Create(fileName))
				{
					BinaryFormatter formatter = GetFormatter();

					formatter.Serialize(stream, mDataModel);
				}
			}
		}

		private BinaryFormatter GetFormatter()
		{
			BinaryFormatter formatter = new BinaryFormatter();
			SurrogateSelector selector = new SurrogateSelector();
			selector.AddSurrogate(typeof(PcapHeader), formatter.Context, new PcapHeaderSurrogate());
			formatter.SurrogateSelector = selector;
			return formatter;
		}

		private class PcapHeaderSurrogate : ISerializationSurrogate
		{
			public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
			{
				PcapHeader header = (PcapHeader)obj;

				info.AddValue("CaptureLength", header.CaptureLength);
				info.AddValue("MicroSeconds", header.MicroSeconds);
				info.AddValue("PacketLength", header.PacketLength);
				info.AddValue("Seconds", header.Seconds);
			}

			public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
			{
				PcapHeader header = new PcapHeader
				{
					CaptureLength = (int)info.GetValue("CaptureLength", typeof(int)),
					MicroSeconds = (int)info.GetValue("MicroSeconds", typeof(int)),
					PacketLength = (int)info.GetValue("PacketLength", typeof(int)),
					Seconds = (int)info.GetValue("Seconds", typeof(int))
				};

				return header;
			}
		}

        private void mResultsView_RowEnter(object sender, DataGridViewCellEventArgs e)
        {
            selected = mDataModel[e.RowIndex];
            richTextBox1.Text = null;
            richTextBox1.AppendText("Acknowledgement Number: " + mDataModel[e.RowIndex].AcknowledgementNumber.ToString() + "\n");
            richTextBox1.AppendText("Sequence Number: " + mDataModel[e.RowIndex].SequenceNumber.ToString() + "\n");
            richTextBox1.AppendText("Ack: " + mDataModel[e.RowIndex].Ack.ToString() + "\n");
            richTextBox1.AppendText("Fin: " + mDataModel[e.RowIndex].Fin.ToString() + "\n");
            richTextBox1.AppendText("Rst: " + mDataModel[e.RowIndex].Rst.ToString() + "\n");
            richTextBox1.AppendText("Syn: " + mDataModel[e.RowIndex].Syn.ToString() + "\n");
            richTextBox1.AppendText("Urg: " + mDataModel[e.RowIndex].Urg.ToString() + "\n");
            richTextBox1.AppendText("Checksum: " + mDataModel[e.RowIndex].Checksum.ToString() + "\n");
            richTextBox1.AppendText("Fragment Flags: " + mDataModel[e.RowIndex].FragmentFlags.ToString() + "\n");
            richTextBox1.AppendText("IP Checksun: " + mDataModel[e.RowIndex].IPChecksum.ToString() + "\n");
            richTextBox1.AppendText("IP Protocol: " + mDataModel[e.RowIndex].IPProtocol.ToString() + "\n");
            richTextBox1.AppendText("TCP Checksum: " + mDataModel[e.RowIndex].TCPChecksum.ToString() + "\n");
            richTextBox1.AppendText("Time to Live: " + mDataModel[e.RowIndex].TimeToLive.ToString() + "\n");

            if (mDataModel[e.RowIndex].Syn)
            {
                richTextBox1.AppendText("Syn = True --> This packet is starting a connection...");
            }
            if (mDataModel[e.RowIndex].Fin)
            {
                richTextBox1.AppendText("Fin = True --> This packet is closing a connection...");
            }
            if (mDataModel[e.RowIndex].Rst)
            {
                richTextBox1.AppendText("Rst = True --> This packet is reestablishing a connection...");
            }
            if (!mDataModel[e.RowIndex].Rst && !mDataModel[e.RowIndex].Fin && !mDataModel[e.RowIndex].Syn)
            {
                richTextBox1.AppendText("This packet contains user data...");
            }
        }

        private List<TCPPacket> getConnectionPackets(string sourceAdress, int sourcePort, string destinationAdress, int destinationPort)
        {
            List<TCPPacket> packets = new List<TCPPacket>();

            foreach(TCPPacket pack in mDataModel)
            {
                if (((pack.SourceAddress.CompareTo(sourceAdress) == 0 && pack.SourcePort == sourcePort) &&
                    (pack.DestinationAddress.CompareTo(destinationAdress) == 0 && pack.DestinationPort == destinationPort)) ||
                   ((pack.SourceAddress.CompareTo(destinationAdress) == 0 && pack.SourcePort == destinationPort) &&
                    (pack.DestinationAddress.CompareTo(sourceAdress) == 0 && pack.DestinationPort == sourcePort)))
                {
                    packets.Add(pack);
                }
            }
            return packets;
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Do you wish to see the draw referent to this connection?", "TCP Package Sniffer", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

            if (result == DialogResult.Yes)
            {
                new DrawForm(getConnectionPackets(selected.SourceAddress, selected.SourcePort, selected.DestinationAddress, selected.DestinationPort)).Show();
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
	}
}
